﻿namespace PicasaDownloader
{
    using System;
    using System.ComponentModel;
    using System.Runtime.InteropServices;
    using System.Windows.Forms;

    /// <summary>
    /// See http://stackoverflow.com/questions/254129/how-to-i-display-a-sort-arrow-in-the-header-of-a-list-view-column-using-c
    /// </summary>
    [EditorBrowsable(EditorBrowsableState.Never)]
    public static class ListViewExtensions
    {
        #region Fields

        /// <summary>
        /// See Win32 Api.
        /// </summary>
        internal const int HDM_FIRST = 0x1200;

        /// <summary>
        /// See Win32 Api.
        /// </summary>
        internal const int HDM_SETITEM = HDM_FIRST + 12;

        /// <summary>
        /// See Win32 Api.
        /// </summary>
        internal const int LVM_FIRST = 0x1000;

        /// <summary>
        /// See Win32 Api.
        /// </summary>
        internal const int LVM_GETHEADER = LVM_FIRST + 31;

        /// <summary>
        /// See Win32 Api.
        /// </summary>
        private const int HDM_GETITEM = HDM_FIRST + 11;

        #endregion Fields

        #region Methods

        /// <summary>
        /// See Win32 Api.
        /// </summary>
        public static void SetSortIcon(this ListView listViewControl, int columnIndex, SortOrder order)
        {
            IntPtr columnHeader = SendMessage(listViewControl.Handle, LVM_GETHEADER, IntPtr.Zero, IntPtr.Zero);
            for (int columnNumber = 0; columnNumber <= listViewControl.Columns.Count - 1; columnNumber++)
            {
                var columnPtr = new IntPtr(columnNumber);
                var item = new HDITEM
                {
                    mask = HDITEM.Mask.Format
                };

                if (SendMessage(columnHeader, HDM_GETITEM, columnPtr, ref item) == IntPtr.Zero)
                {
                    throw new Win32Exception();
                }

                if (order != SortOrder.None && columnNumber == columnIndex)
                {
                    switch (order)
                    {
                        case SortOrder.Ascending:
                            item.fmt &= ~HDITEM.Format.SortDown;
                            item.fmt |= HDITEM.Format.SortUp;
                            break;
                        case SortOrder.Descending:
                            item.fmt &= ~HDITEM.Format.SortUp;
                            item.fmt |= HDITEM.Format.SortDown;
                            break;
                    }
                }
                else
                {
                    item.fmt &= ~HDITEM.Format.SortDown & ~HDITEM.Format.SortUp;
                }

                if (SendMessage(columnHeader, HDM_SETITEM, columnPtr, ref item) == IntPtr.Zero)
                {
                    throw new Win32Exception();
                }
            }
        }

        /// <summary>
        /// See Win32 Api.
        /// </summary>
        [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        internal static extern IntPtr SendMessage(IntPtr hWnd, UInt32 msg, IntPtr wParam, IntPtr lParam);

        /// <summary>
        /// See Win32 Api.
        /// </summary>
        [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        internal static extern IntPtr SendMessage(IntPtr hWnd, UInt32 msg, IntPtr wParam, ref HDITEM lParam);

        #endregion Methods

        #region Nested Types

        /// <summary>
        /// See Win32 Api.
        /// </summary>
        [StructLayout(LayoutKind.Sequential)]
        internal struct HDITEM
        {
            /// <summary>
            /// See Win32 Api.
            /// </summary>
            internal Mask mask;

            /// <summary>
            /// See Win32 Api.
            /// </summary>
            internal int cxy;

            /// <summary>
            /// See Win32 Api.
            /// </summary>
            [MarshalAs(UnmanagedType.LPTStr)]
            internal string pszText;

            /// <summary>
            /// See Win32 Api.
            /// </summary>
            internal IntPtr hbm;

            /// <summary>
            /// See Win32 Api.
            /// </summary>
            internal int cchTextMax;

            /// <summary>
            /// See Win32 Api.
            /// </summary>
            internal Format fmt;

            /// <summary>
            /// See Win32 Api.
            /// </summary>
            internal IntPtr lParam;

            // _WIN32_IE >= 0x0300
            /// <summary>
            /// See Win32 Api.
            /// </summary>
            internal int iImage;

            /// <summary>
            /// See Win32 Api.
            /// </summary>
            internal int iOrder;

            // _WIN32_IE >= 0x0500
            /// <summary>
            /// See Win32 Api.
            /// </summary>
            internal uint type;

            /// <summary>
            /// See Win32 Api.
            /// </summary>
            internal IntPtr pvFilter;

            // _WIN32_WINNT >= 0x0600
            /// <summary>
            /// See Win32 Api.
            /// </summary>
            internal uint state;

            #region Enumerations

            /// <summary>
            /// See Win32 Api.
            /// </summary>
            [Flags]
            internal enum Format
            {
                /// <summary>
                /// HDF_SORTDOWN, See Win32 Api.
                /// </summary>
                SortDown = 0x200,

                /// <summary>
                /// HDF_SORTUP, See Win32 Api.
                /// </summary>
                SortUp = 0x400,
            }

            /// <summary>
            /// See Win32 Api.
            /// </summary>
            [Flags]
            internal enum Mask
            {
                /// <summary>
                /// HDI_FORMAT, See Win32 Api.
                /// </summary>
                Format = 0x4,
            }

            #endregion Enumerations
        }

        #endregion Nested Types
    }
}